home *** CD-ROM | disk | FTP | other *** search
/ EuroCD 3 / EuroCD 3.iso / Programming / Python1.4_Source / Lib / bdb.py < prev    next >
Text File  |  1998-06-24  |  10KB  |  368 lines

  1. # A generic Python debugger base class.
  2. # This class takes care of details of the trace facility;
  3. # a derived class should implement user interaction.
  4. # There are two debuggers based upon this:
  5. # 'pdb', a text-oriented debugger not unlike dbx or gdb;
  6. # and 'wdb', a window-oriented debugger.
  7. # And of course... you can roll your own!
  8.  
  9. import sys
  10. import types
  11.  
  12. BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
  13.  
  14.  
  15. class Bdb: # Basic Debugger
  16.     
  17.     def __init__(self):
  18.         self.breaks = {}
  19.     
  20.     def reset(self):
  21.         import linecache
  22.         linecache.checkcache()
  23.         self.botframe = None
  24.         self.stopframe = None
  25.         self.returnframe = None
  26.         self.quitting = 0
  27.     
  28.     def trace_dispatch(self, frame, event, arg):
  29.         if self.quitting:
  30.             return # None
  31.         if event == 'line':
  32.             return self.dispatch_line(frame)
  33.         if event == 'call':
  34.             return self.dispatch_call(frame, arg)
  35.         if event == 'return':
  36.             return self.dispatch_return(frame, arg)
  37.         if event == 'exception':
  38.             return self.dispatch_exception(frame, arg)
  39.         print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
  40.         return self.trace_dispatch
  41.     
  42.     def dispatch_line(self, frame):
  43.         if self.stop_here(frame) or self.break_here(frame):
  44.             self.user_line(frame)
  45.             if self.quitting: raise BdbQuit
  46.         return self.trace_dispatch
  47.     
  48.     def dispatch_call(self, frame, arg):
  49.         frame.f_locals['__args__'] = arg
  50.         if self.botframe is None:
  51.             # First call of dispatch since reset()
  52.             self.botframe = frame
  53.             return self.trace_dispatch
  54.         if not (self.stop_here(frame) or self.break_anywhere(frame)):
  55.             # No need to trace this function
  56.             return # None
  57.         self.user_call(frame, arg)
  58.         if self.quitting: raise BdbQuit
  59.         return self.trace_dispatch
  60.     
  61.     def dispatch_return(self, frame, arg):
  62.         if self.stop_here(frame) or frame == self.returnframe:
  63.             self.user_return(frame, arg)
  64.             if self.quitting: raise BdbQuit
  65.     
  66.     def dispatch_exception(self, frame, arg):
  67.         if self.stop_here(frame):
  68.             self.user_exception(frame, arg)
  69.             if self.quitting: raise BdbQuit
  70.         return self.trace_dispatch
  71.     
  72.     # Normally derived classes don't override the following
  73.     # methods, but they may if they want to redefine the
  74.     # definition of stopping and breakpoints.
  75.     
  76.     def stop_here(self, frame):
  77.         if self.stopframe is None:
  78.             return 1
  79.         if frame is self.stopframe:
  80.             return 1
  81.         while frame is not None and frame is not self.stopframe:
  82.             if frame is self.botframe:
  83.                 return 1
  84.             frame = frame.f_back
  85.         return 0
  86.     
  87.     def break_here(self, frame):
  88.         if not self.breaks.has_key(frame.f_code.co_filename):
  89.             return 0
  90.         if not frame.f_lineno in \
  91.                 self.breaks[frame.f_code.co_filename]:
  92.             return 0
  93.         return 1
  94.     
  95.     def break_anywhere(self, frame):
  96.         return self.breaks.has_key(frame.f_code.co_filename)
  97.     
  98.     # Derived classes should override the user_* methods
  99.     # to gain control.
  100.     
  101.     def user_call(self, frame, argument_list):
  102.         # This method is called when there is the remote possibility
  103.         # that we ever need to stop in this function
  104.         pass
  105.     
  106.     def user_line(self, frame):
  107.         # This method is called when we stop or break at this line
  108.         pass
  109.     
  110.     def user_return(self, frame, return_value):
  111.         # This method is called when a return trap is set here
  112.         pass
  113.     
  114.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  115.         # This method is called if an exception occurs,
  116.         # but only if we are to stop at or just below this level
  117.         pass
  118.     
  119.     # Derived classes and clients can call the following methods
  120.     # to affect the stepping state.
  121.     
  122.     def set_step(self):
  123.         # Stop after one line of code
  124.         self.stopframe = None
  125.         self.returnframe = None
  126.         self.quitting = 0
  127.     
  128.     def set_next(self, frame):
  129.         # Stop on the next line in or below the given frame
  130.         self.stopframe = frame
  131.         self.returnframe = None
  132.         self.quitting = 0
  133.     
  134.     def set_return(self, frame):
  135.         # Stop when returning from the given frame
  136.         self.stopframe = frame.f_back
  137.         self.returnframe = frame
  138.         self.quitting = 0
  139.     
  140.     def set_trace(self):
  141.         # Start debugging from here
  142.         try:
  143.             1 + ''
  144.         except:
  145.             frame = sys.exc_traceback.tb_frame.f_back
  146.         self.reset()
  147.         while frame:
  148.             frame.f_trace = self.trace_dispatch
  149.             self.botframe = frame
  150.             frame = frame.f_back
  151.         self.set_step()
  152.         sys.settrace(self.trace_dispatch)
  153.  
  154.     def set_continue(self):
  155.         # Don't stop except at breakpoints or when finished
  156.         self.stopframe = self.botframe
  157.         self.returnframe = None
  158.         self.quitting = 0
  159.         if not self.breaks:
  160.             # no breakpoints; run without debugger overhead
  161.             sys.settrace(None)
  162.             try:
  163.                 1 + ''    # raise an exception
  164.             except:
  165.                 frame = sys.exc_traceback.tb_frame.f_back
  166.             while frame and frame is not self.botframe:
  167.                 del frame.f_trace
  168.                 frame = frame.f_back
  169.     
  170.     def set_quit(self):
  171.         self.stopframe = self.botframe
  172.         self.returnframe = None
  173.         self.quitting = 1
  174.         sys.settrace(None)
  175.     
  176.     # Derived classes and clients can call the following methods
  177.     # to manipulate breakpoints.  These methods return an
  178.     # error message is something went wrong, None if all is well.
  179.     # Call self.get_*break*() to see the breakpoints.
  180.     
  181.     def set_break(self, filename, lineno):
  182.         import linecache # Import as late as possible
  183.         line = linecache.getline(filename, lineno)
  184.         if not line:
  185.             return 'That line does not exist!'
  186.         if not self.breaks.has_key(filename):
  187.             self.breaks[filename] = []
  188.         list = self.breaks[filename]
  189.         if lineno in list:
  190.             return 'There is already a breakpoint there!'
  191.         list.append(lineno)
  192.     
  193.     def clear_break(self, filename, lineno):
  194.         if not self.breaks.has_key(filename):
  195.             return 'There are no breakpoints in that file!'
  196.         if lineno not in self.breaks[filename]:
  197.             return 'There is no breakpoint there!'
  198.         self.breaks[filename].remove(lineno)
  199.         if not self.breaks[filename]:
  200.             del self.breaks[filename]
  201.     
  202.     def clear_all_file_breaks(self, filename):
  203.         if not self.breaks.has_key(filename):
  204.             return 'There are no breakpoints in that file!'
  205.         del self.breaks[filename]
  206.     
  207.     def clear_all_breaks(self):
  208.         if not self.breaks:
  209.             return 'There are no breakpoints!'
  210.         self.breaks = {}
  211.     
  212.     def get_break(self, filename, lineno):
  213.         return self.breaks.has_key(filename) and \
  214.             lineno in self.breaks[filename]
  215.     
  216.     def get_file_breaks(self, filename):
  217.         if self.breaks.has_key(filename):
  218.             return self.breaks[filename]
  219.         else:
  220.             return []
  221.     
  222.     def get_all_breaks(self):
  223.         return self.breaks
  224.     
  225.     # Derived classes and clients can call the following method
  226.     # to get a data structure representing a stack trace.
  227.     
  228.     def get_stack(self, f, t):
  229.         stack = []
  230.         if t and t.tb_frame is f:
  231.             t = t.tb_next
  232.         while f is not None:
  233.             stack.append((f, f.f_lineno))
  234.             if f is self.botframe:
  235.                 break
  236.             f = f.f_back
  237.         stack.reverse()
  238.         i = max(0, len(stack) - 1)
  239.         while t is not None:
  240.             stack.append((t.tb_frame, t.tb_lineno))
  241.             t = t.tb_next
  242.         return stack, i
  243.     
  244.     # 
  245.     
  246.     def format_stack_entry(self, frame_lineno, lprefix=': '):
  247.         import linecache, repr, string
  248.         frame, lineno = frame_lineno
  249.         filename = frame.f_code.co_filename
  250.         s = filename + '(' + `lineno` + ')'
  251.         if frame.f_code.co_name:
  252.             s = s + frame.f_code.co_name
  253.         else:
  254.             s = s + "<lambda>"
  255.         if frame.f_locals.has_key('__args__'):
  256.             args = frame.f_locals['__args__']
  257.         else:
  258.             args = None
  259.         if args:
  260.             s = s + repr.repr(args)
  261.         else:
  262.             s = s + '()'
  263.         if frame.f_locals.has_key('__return__'):
  264.             rv = frame.f_locals['__return__']
  265.             s = s + '->'
  266.             s = s + repr.repr(rv)
  267.         line = linecache.getline(filename, lineno)
  268.         if line: s = s + lprefix + string.strip(line)
  269.         return s
  270.     
  271.     # The following two methods can be called by clients to use
  272.     # a debugger to debug a statement, given as a string.
  273.     
  274.     def run(self, cmd, globals=None, locals=None):
  275.         if globals is None:
  276.             import __main__
  277.             globals = __main__.__dict__
  278.         if locals is None:
  279.             locals = globals
  280.         self.reset()
  281.         sys.settrace(self.trace_dispatch)
  282.         if type(cmd) <> types.CodeType:
  283.             cmd = cmd+'\n'
  284.         try:
  285.             try:
  286.                 exec cmd in globals, locals
  287.             except BdbQuit:
  288.                 pass
  289.         finally:
  290.             self.quitting = 1
  291.             sys.settrace(None)
  292.     
  293.     def runeval(self, expr, globals=None, locals=None):
  294.         if globals is None:
  295.             import __main__
  296.             globals = __main__.__dict__
  297.         if locals is None:
  298.             locals = globals
  299.         self.reset()
  300.         sys.settrace(self.trace_dispatch)
  301.         if type(expr) <> types.CodeType:
  302.             expr = expr+'\n'
  303.         try:
  304.             try:
  305.                 return eval(expr, globals, locals)
  306.             except BdbQuit:
  307.                 pass
  308.         finally:
  309.             self.quitting = 1
  310.             sys.settrace(None)
  311.  
  312.     def runctx(self, cmd, globals, locals):
  313.         # B/W compatibility
  314.         self.run(cmd, globals, locals)
  315.  
  316.     # This method is more useful to debug a single function call.
  317.  
  318.     def runcall(self, func, *args):
  319.         self.reset()
  320.         sys.settrace(self.trace_dispatch)
  321.         res = None
  322.         try:
  323.             try:
  324.                 res = apply(func, args)
  325.             except BdbQuit:
  326.                 pass
  327.         finally:
  328.             self.quitting = 1
  329.             sys.settrace(None)
  330.         return res
  331.  
  332.  
  333. def set_trace():
  334.     Bdb().set_trace()
  335.  
  336. # -------------------- testing --------------------
  337.  
  338. class Tdb(Bdb):
  339.     def user_call(self, frame, args):
  340.         name = frame.f_code.co_name
  341.         if not name: name = '???'
  342.         print '+++ call', name, args
  343.     def user_line(self, frame):
  344.         import linecache, string
  345.         name = frame.f_code.co_name
  346.         if not name: name = '???'
  347.         fn = frame.f_code.co_filename
  348.         line = linecache.getline(fn, frame.f_lineno)
  349.         print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
  350.     def user_return(self, frame, retval):
  351.         print '+++ return', retval
  352.     def user_exception(self, frame, exc_stuff):
  353.         print '+++ exception', exc_stuff
  354.         self.set_continue()
  355.  
  356. def foo(n):
  357.     print 'foo(', n, ')'
  358.     x = bar(n*10)
  359.     print 'bar returned', x
  360.  
  361. def bar(a):
  362.     print 'bar(', a, ')'
  363.     return a/2
  364.  
  365. def test():
  366.     t = Tdb()
  367.     t.run('import bdb; bdb.foo(10)')
  368.